/**
 * 线段高程分析器类
 * 用于寻找由两个经纬度连成的线段两侧指定距离范围内的最高高程点
 * 
 * 实现原理：
 * 1. 使用球面几何算法计算两点连线的方位角
 * 2. 在连线上均匀生成多个采样点
 * 3. 对每个采样点，向线段两侧（垂直方向）生成采样点
 * 4. 计算所有采样点的高程，找出最高点
 */
class LineHighestPointAnalyzer {
    constructor(demReader) {
        this.demReader = demReader;
        // 地球半径（公里）
        this.EARTH_RADIUS = 6371.0;
    }

    /**
     * 将角度转换为弧度
     * @param {number} degrees - 角度值
     * @returns {number} 弧度值
     */
    toRadians(degrees) {
        return degrees * Math.PI / 180.0;
    }

    /**
     * 将弧度转换为角度
     * @param {number} radians - 弧度值
     * @returns {number} 角度值
     */
    toDegrees(radians) {
        return radians * 180.0 / Math.PI;
    }

    /**
     * 计算两点之间的方位角
     * @param {number} lat1 - 起点纬度
     * @param {number} lon1 - 起点经度
     * @param {number} lat2 - 终点纬度
     * @param {number} lon2 - 终点经度
     * @returns {number} 方位角（弧度）
     */
    calculateBearing(lat1, lon1, lat2, lon2) {
        const phi1 = this.toRadians(lat1);
        const phi2 = this.toRadians(lat2);
        const deltaLambda = this.toRadians(lon2 - lon1);

        const y = Math.sin(deltaLambda) * Math.cos(phi2);
        const x = Math.cos(phi1) * Math.sin(phi2) - 
                  Math.sin(phi1) * Math.cos(phi2) * Math.cos(deltaLambda);
        const theta = Math.atan2(y, x);

        return theta;
    }

    /**
     * 从给定点向指定方向移动指定距离，计算新点的经纬度
     * @param {number} lat - 起始纬度
     * @param {number} lon - 起始经度
     * @param {number} bearing - 方位角（弧度）
     * @param {number} distance - 距离（公里）
     * @returns {object} 包含新的纬度和经度的对象
     */
    calculateDestination(lat, lon, bearing, distance) {
        const phi1 = this.toRadians(lat);
        const lambda1 = this.toRadians(lon);
        const angularDistance = distance / this.EARTH_RADIUS;

        const phi2 = Math.asin(
            Math.sin(phi1) * Math.cos(angularDistance) +
            Math.cos(phi1) * Math.sin(angularDistance) * Math.cos(bearing)
        );

        const lambda2 = lambda1 + Math.atan2(
            Math.sin(bearing) * Math.sin(angularDistance) * Math.cos(phi1),
            Math.cos(angularDistance) - Math.sin(phi1) * Math.sin(phi2)
        );

        return {
            lat: this.toDegrees(phi2),
            lon: this.toDegrees(lambda2)
        };
    }

    /**
     * 计算两点之间的距离（公里）
     * @param {number} lat1 - 第一点纬度
     * @param {number} lon1 - 第一点经度
     * @param {number} lat2 - 第二点纬度
     * @param {number} lon2 - 第二点经度
     * @returns {number} 距离（公里）
     */
    calculateDistance(lat1, lon1, lat2, lon2) {
        const phi1 = this.toRadians(lat1);
        const phi2 = this.toRadians(lat2);
        const deltaPhi = this.toRadians(lat2 - lat1);
        const deltaLambda = this.toRadians(lon2 - lon1);

        const a = Math.sin(deltaPhi / 2) * Math.sin(deltaPhi / 2) +
                  Math.cos(phi1) * Math.cos(phi2) *
                  Math.sin(deltaLambda / 2) * Math.sin(deltaLambda / 2);
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        return this.EARTH_RADIUS * c;
    }

    /**
     * 在两点连线上均匀生成采样点
     * @param {number} lat1 - 起点纬度
     * @param {number} lon1 - 起点经度
     * @param {number} lat2 - 终点纬度
     * @param {number} lon2 - 终点经度
     * @param {number} numPoints - 采样点数量
     * @returns {array} 采样点数组，每个点包含lat和lon
     */
    generateSamplePointsAlongLine(lat1, lon1, lat2, lon2, numPoints = 50) {
        const points = [];
        
        for (let i = 0; i <= numPoints; i++) {
            const ratio = i / numPoints;
            
            // 线性插值计算中间点
            const lat = lat1 + (lat2 - lat1) * ratio;
            const lon = lon1 + (lon2 - lon1) * ratio;
            
            points.push({ lat, lon });
        }
        
        return points;
    }

    /**
     * 寻找两点连线两侧指定距离范围内的最高高程点
     * @param {number} lat1 - 起点纬度
     * @param {number} lon1 - 起点经度
     * @param {number} lat2 - 终点纬度
     * @param {number} lon2 - 终点经度
     * @param {number} distanceKm - 搜索距离（公里），默认为25公里
     * @returns {object} 包含最高点的经纬度和高程的对象
     */
    findHighestPoint(lat1, lon1, lat2, lon2, distanceKm = 25) {
        if (!this.demReader.dataLoaded) {
            console.error('DEM数据未加载');
            return null;
        }

        // 计算主线段的方位角（从起点指向终点的方向）
        const mainBearing = this.calculateBearing(lat1, lon1, lat2, lon2);
        // 计算垂直于主线段的两个方向的方位角（90度和270度）
        const perpendicularBearing1 = mainBearing + Math.PI / 2;
        const perpendicularBearing2 = mainBearing - Math.PI / 2;

        // 在主线上生成50个均匀分布的采样点，这样可以覆盖整条线段
        const mainPoints = this.generateSamplePointsAlongLine(lat1, lon1, lat2, lon2, 50);
        
        let highestPoint = null;
        let maxElevation = -Infinity;

        // 对每个主线上的点，向两侧垂直方向生成采样点并计算高程
        for (const point of mainPoints) {
            // 生成垂直于主线的左右两个端点（距离主线段distanceKm公里）
            const leftPoint = this.calculateDestination(point.lat, point.lon, perpendicularBearing1, distanceKm);
            const rightPoint = this.calculateDestination(point.lat, point.lon, perpendicularBearing2, distanceKm);
            
            // 在左右两点之间生成20个采样点以获得更精确的结果
            // 这样可以覆盖从左侧边界到右侧边界的整个区域
            const crossPoints = this.generateSamplePointsAlongLine(
                leftPoint.lat, leftPoint.lon, rightPoint.lat, rightPoint.lon, 20
            );
            
            // 检查主点和所有交叉点的高程
            const allPointsToCheck = [point, ...crossPoints];
            
            for (const checkPoint of allPointsToCheck) {
                // 检查点是否在DEM数据范围内
                const gridPos = this.demReader.latLonToGrid(checkPoint.lon, checkPoint.lat);
                if (gridPos) {
                    // 计算该点的高程
                    const elevation = this.demReader.calculateElevation(checkPoint.lon, checkPoint.lat);
                    
                    // 更新最高点信息
                    if (elevation > maxElevation && elevation !== this.demReader.NODATA_VALUE) {
                        maxElevation = elevation;
                        highestPoint = {
                            latitude: checkPoint.lat,
                            longitude: checkPoint.lon,
                            elevation: elevation
                        };
                    }
                }
            }
        }

        // 返回找到的最高点信息，如果没有找到有效点则返回null
        return highestPoint;
    }
}